/**
 * File:     wiidevicedriver.mm
 * Authors:  Dominic Stalder, Thomas Bruederli
 *
 * Implementation of the mac specific wii device (wii balance board and wii controller) driver
 * in objective-c++. This class acts as a callback object for the <code>WiiDevice</code> class
 * and emits the important signals. It connects the wii devices based on the DarwiinRemote
 * framework.
 *
 * This file is part of the Qt-based cross-platform Wii device driver.
 *
 * Copyright (C) 2009-2010, Dominic Stalder & Thomas Bruederli
 * Licensed under the GNU Lesser General Public License (LGPL) as published
 * by the Free Software Foundation at http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * $Id: wiidevicedriver.mm 5 2010-03-03 09:10:12Z roundcube $
 */

#include "wiidevice.h"
#include "wiidevicedriver.h"

/** 
 * Class constructor.
 *
 * Initializes a new <code>WiiDevice</code> object.
 *
 * @param  type  specify if the device is a wii balance board or a wii controller
 */
WiiDeviceDriver::WiiDeviceDriver(int type) : deviceType(type), driverState(WiiDeviceDriver::STATE_NOTRUNNING)
{
	// creates a new wii device object and allocs memory for the device
	wiiDevice = [[WiiDevice alloc] initWithCallbackAndType: this: type];
	
	// sets the connection status to not connected
	setConnectionState(WiiDeviceDriver::STATE_NOTCONNECTED);
}

/** 
 * Class destructor.
 *
 * Releases the <code>WiiDevice</code> object.
 */
WiiDeviceDriver::~WiiDeviceDriver()
{
	// sets the connection status to not connected
	setConnectionState(WiiDeviceDriver::STATE_NOTCONNECTED);
	
	// releases the wii device and frees the allocated memory
	[(WiiDevice*)wiiDevice release];
}

/**
 * Impelements the run loop of the inherited <code>QThread</code> class and starts
 * connecting to the wii device.
 */
void WiiDeviceDriver::run()
{
	// sends a log message
	setLogMessage(tr("Driver started."));
	
	// sets the driver state to running
	driverState = WiiDeviceDriver::STATE_RUNNING;
	
	// creates an auto release pool - if the pool is released, all the objects inside are also released
	NSAutoreleasePool *autoReleasePool = [[NSAutoreleasePool alloc] init];
	
	// connects to the wii device
	if ([(WiiDevice*)wiiDevice connect])
	{
		// sends a log message
		setLogMessage(tr("Driver starts connecting..."));
		
		while (driverState == WiiDeviceDriver::STATE_RUNNING)
		{
			// enters the run loop of the qthread
			[ [NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1] ];
		}		
	}	
	else	// failed to connect - maybe bluetooth is not available or turned off
	{
		setLogMessage("WiiRemoteDiscovery failed to connect");
		
		// inform user or whoever is out there
		setInstruction(WiiDeviceDriver::MESSAGE_DISCOVERYERROR);
		
		// set state as not running
		driverState = WiiDeviceDriver::STATE_NOTRUNNING;
		setConnectionState(WiiDeviceDriver::STATE_NOTRUNNING);
	}

	
	// releases the auto release pool and all the objects inside too
	[autoReleasePool release];
}

/**
 * Stops the run loop of the inherited <code>QThread</code> class.
 */
void WiiDeviceDriver::stop()
{
	// ignore if not running
	if (driverState == WiiDeviceDriver::STATE_NOTRUNNING)
		return;
	
	// sends a log message
	setLogMessage(tr("Driver stopped."));

	// sets the connection status to not connected
	setConnectionState(WiiDeviceDriver::STATE_NOTCONNECTED);
	
	// disconnects from the wii device
	[(WiiDevice*)wiiDevice disconnect];
	
	// sets the driver state to not running
	driverState = WiiDeviceDriver::STATE_NOTRUNNING;
}

/**
 * Sets the connection state of the wii device.
 *
 * @param  state  connection state of the wii device
 */
void WiiDeviceDriver::setConnectionState(int connectionState)
{	
	// sets the connection state of the wii device state
	deviceState.connectionState = connectionState;

	// sends a log message
	setLogMessage(tr("Driver changed connection state."));
	
	// emits the signal that the connection state has changed
	emit connectionStateChanged(deviceType, connectionState);
}

/**
 * Returns true if the wii device is connected.
 *
 * @return  wii device connection state
 */
int WiiDeviceDriver::isConnected()
{
	// returns true if the wii device is connected
	return deviceState.connectionState == WiiDeviceDriver::STATE_CONNECTED ? true : false;
}

/**
 * Returns the address of the <code>wiiDeviceState</code> struct with the wii device state.
 *
 * @return  <code>wiiDeviceState</code> struct with the wii device state
 */
WiiDeviceState& WiiDeviceDriver::getDeviceState()
{
	return deviceState;
}

/**
 * Emits the signal that the instruction has been changed.
 *
 * @param  instruction  intstruction text to be displayed
 */
void WiiDeviceDriver::setInstruction(const unsigned char instruction)
{
	// emits signal that the instruction has been changed
	emit instructionChanged(deviceType, WiiDeviceDriver::messageString(instruction));
}

/**
 * Emits the signal that a log message has been generated.
 *
 * @param  logMessage  text of the log message as a char
 */
void WiiDeviceDriver::setLogMessage(QString logMessage)
{
	// emits signal that a log message has been generated
	emit logMessageGenerated(QString(logMessage), deviceType, deviceState.connectionState);
}

/**
 * Sets the button pressed state in the <code>wiiDeviceState</code> struct with the wii device state
 * and emits the signal that the button state has changed.
 *
 * @param  which  specifies the button that has changed
 * @param  state  specifies the button state
 */
void WiiDeviceDriver::setButtonPressed(int which, int state)
{
	// sets/unsets the button bit of the wii device state
	if (state)
		deviceState.buttonPressed |= which;
	else
		deviceState.buttonPressed &= ~which;
	
	// emits signal that the button state has changed
	emit buttonChanged(which, state);
}

/**
 * Sets the values in the <code>wiiDeviceState</code> struct with the wii device state to the captured
 * values in kg and emits the signal, that the values have changed.
 *
 * @param  topLeft      value of the top left quadrant
 * @param  topRight     value of the top right quadrant
 * @param  bottomRight  value of the bottom right quadrant
 * @param  bottomLeft   value of the bottom left quadrant
 */
void WiiDeviceDriver::setKilograms(float topLeft, float topRight, float bottomRight, float bottomLeft)
{
	// sets the values of in the wii device to the max of zero and the value respectively
	deviceState.topLeft = MAX(0, topLeft);
	deviceState.topRight = MAX(0, topRight);
	deviceState.bottomRight = MAX(0, bottomRight);
	deviceState.bottomLeft = MAX(0, bottomLeft);
	
	// sums all the values
	deviceState.totalWeight = topLeft + topRight + bottomRight + bottomLeft;
	
	// defines a minima for very low values and sets all the values to 0
	if (deviceState.totalWeight < 8)
		deviceState.topLeft = deviceState.topRight = deviceState.bottomRight = deviceState.bottomLeft = deviceState.totalWeight = 0;
	
	// emits the signal that the values has changed
	emit kilogramsChanged(deviceState.topLeft, deviceState.topRight, deviceState.bottomRight, deviceState.bottomLeft, deviceState.totalWeight);
}

/**
 * Emits the signal, that the acceleration values have changed.
 *
 * @param  roll   acceleration on X axis
 * @param  pitch  acceleration on Y axis
 */
void WiiDeviceDriver::setAcceleration(double roll, double pitch, double yaw)
{
	emit orientationChanged(roll, pitch, yaw);
}

/**
 * Sets the battery level in the <code>wiiDeviceState</code> struct with the wii device state
 * and emits the signal that the battery level has changed
 *
 * @param  batteryLevel  level of the battery in percent
 */
void WiiDeviceDriver::setBatteryLevel(float batteryLevel)
{
	// sets the battery level of the wii device state
	deviceState.batteyLevel = batteryLevel;
}
